package analysisAndCode;

import entity.Word;
import utils.TokenUtil;
import utils.WordUtil;

import java.util.ArrayList;
import java.util.List;

public class Lex {

    List<Word> wordList = new ArrayList<>();
    List<Word> wrongWordList = new ArrayList<>();

    int i = 0;
    int line = 1;

    public List<Word> getWordList() {
        return wordList;
    }

    public void setWordList(List<Word> wordList) {
        this.wordList = wordList;
    }

    public List<Word> getWrongWordList() {
        return wrongWordList;
    }

    public void setWrongWordList(List<Word> wrongWordList) {
        this.wrongWordList = wrongWordList;
    }

    public void OnLex(String txt){

        while (i < txt.length()){
            char ch = txt.charAt(i);
            if(ch >= '0' && ch <= '9'){
                regDigit(txt);
            } else if (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
                regId(txt);
            } else if (ch == '/') {
                regExegesis(txt);
            } else if (TokenUtil.isSignalOperator(ch)) {
                regOperator(txt);
            } else if (TokenUtil.isBoundary(ch)) {
                addBoundary(txt);
            } else if (ch == '\n') {
                line++;
                i++;
            } else if (ch == '\'') {
                regSingleCharacter(txt);
            } else{
                i++;
            }
        }
//        for (int j = 0; j < wordList.size(); j++) {
//            System.out.println(wordList.get(j).toString());
//        }
    }

    private void regSingleCharacter(String txt) {
        int start = i;
        i++;
        i++;
        if (txt.charAt(i) == '\''){
            i++;
            String word = txt.substring(start, i);
            int code = TokenUtil.getTokenCode("字符常量");
            wordList.add(new Word(line, word, code));
        }else {
            String word = txt.substring(start, i);
            wordList.add(new Word(line, word, -1));
            wrongWordList.add(new Word(line, word, -1));
        }
    }

    private void addBoundary(String txt) {
        String word = txt.substring(i, i + 1);
        int code = TokenUtil.getTokenCode(word);
        wordList.add(new Word(line, word, code));
        i++;
    }

    private void regOperator(String txt) {
        int start = i;
        int state = 0;
        while (state != 99 && state != 100 && i < txt.length()) {
            char ch = txt.charAt(i);
            switch (state) {
                case 0: {
                    if(ch == '>'){
                        state = 1;
                    } else if (ch == '<') {
                        state = 7;
                    } else if (ch == '+') {
                        state = 14;
                    } else if (ch == '-') {
                        state = 18;
                    } else if (ch == '*') {
                        state = 22;
                    } else if (ch == '%') {
                        state = 25;
                    } else if (ch == '=') {
                        state = 28;
                    } else if (ch == '!') {
                        state = 31;
                    } else if (ch == '&') {
                        state = 34;
                    } else if (ch == '|') {
                        state = 37;
                    }
                    break;
                }
                case 1: {
                    if(ch == '='){
                        state = 2;
                    }else if (ch == '>'){
                        state = 3;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 3: {
                    if(ch == '='){
                        state = 4;
                    }else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 7: {
                    if(ch == '='){
                        state = 8;
                    }else if (ch == '<'){
                        state = 9;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 9: {
                    if(ch == '='){
                        state = 10;
                    }else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 14: {
                    if(ch == '='){
                        state = 15;
                    }else if (ch == '+'){
                        state = 16;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 18: {
                    if(ch == '='){
                        state = 19;
                    }else if (ch == '-'){
                        state = 20;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 22: {
                    if(ch == '='){
                        state = 23;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 25: {
                    if(ch == '='){
                        state = 26;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 28: {
                    if(ch == '='){
                        state = 29;
                    } else if (TokenUtil.isSignalOperator(ch)) {
//                        if(ch == '!'){
//                            state = 99;
//                        }else{
//                            state = 100;
//                        }
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 31: {
                    if(ch == '='){
                        state = 32;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 34: {
                    if(ch == '&'){
                        state = 35;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 37: {
                    if(ch == '|'){
                        state = 38;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
                case 38:
                case 35:
                case 32:
                case 29:
                case 26:
                case 23:
                case 20:
                case 19:
                case 16:
                case 15:
                case 10:
                case 8:
                case 4:
                case 2: {
                    if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    }else {
                        state = 99;
                    }
                    break;
                }
            }
            i++;
        }
        if (state == 100){
            error(txt, start);
            return;
        }
        if (state == 99){
            i--;
            String word = txt.substring(start, i);
            int code = TokenUtil.getTokenCode(word);
            wordList.add(new Word(line, word, code));
            return;
        }
        if(i == txt.length()){
            String word = txt.substring(start);
            int code = TokenUtil.getTokenCode(word);
            wordList.add(new Word(line, word, code));
        }
    }

    private void regExegesis(String txt) {
        int start = i;
        int state = 0;
        while (state != 3 && state != 6 && state != 7 && state != 8 && state != 100 && i < txt.length()){
            char ch = txt.charAt(i);
            switch (state){
                case 0: state = 1; break;
                case 1: {
                    if(ch == '/'){
                        state = 2;
                    } else if (ch == '*') {
                        state = 4;
                    } else if (ch == '=') {
                        state = 7;
                    } else if (TokenUtil.isSignalOperator(ch)) {
                        state = 100;
                    } else {
                        state = 8;
                    }
                    break;
                }
                case 2: {
                    if(ch == '\n'){
                        state = 3;
                    }else {
                        state = 2;
                    }
                    break;
                }
                case 4: {
                   if(ch == '*'){
                       state = 5;
                   }else {
                       state = 4;
                   }
                   break;
                }
                case 5: {
                    if (ch == '*'){
                        state = 5;
                    }else if (ch == '/'){
                        state = 6;
                    } else {
                        state = 4;
                    }
                    break;
                }
            }
            i++;
        }
        if (state == 7 || state == 8){
            if (state == 8){
                i--;
            }
            String word = txt.substring(start, i);
            int code = TokenUtil.getTokenCode(word);
            wordList.add(new Word(line, word, code));
            return;
        }
        if(state == 100){
            error(txt, start);
            return;
        }
        if(i == txt.length()){
            String word = txt.substring(start);
            int code = TokenUtil.getTokenCode(word);
            wordList.add(new Word(line, word, code));
        }
    }

    private void regId(String txt) {
        int start = i;
        int state = 0;
        while (state != 2 && i < txt.length()){
            char ch = txt.charAt(i);
            switch (state){
                case 0: {
                    if(ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')){
                        state = 1;
                    } else {
                        error(txt, start);
                    }
                    break;
                }
                case 1: {
                    if(ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')){
                        state = 1;
                    } else {
                        state = 2;
                    }
                }
            }
            i++;
        }
        if(state == 2){
            i--;
            String word = txt.substring(start, i);
            int code = -1;
            if(TokenUtil.isKeyWord(word)){
                code = TokenUtil.getTokenCode(word);
            }else {
                code = TokenUtil.getTokenCode("标识符");
            }
            wordList.add(new Word(line, word, code));
        }
        if(i == txt.length()){
            String word = txt.substring(start);
            int code = -1;
            if(TokenUtil.isKeyWord(word)){
                code = TokenUtil.getTokenCode(word);
            }else {
                code = TokenUtil.getTokenCode("标识符");
            }
            wordList.add(new Word(line, word, code));
        }

    }

    private void regDigit(String txt) {
        int start = i;
        int state = 0;
        while (state != 4 && state != 7 && state != 13 && state != 14 && state != 15 && state != 16){
            char ch = txt.charAt(i);
            switch (state){
                case 0: {
                    if (ch == '0'){
                        state = 2;
                    }else {
                        state = 1;
                    }
                    break;
                }
                case 1: {
                    if (ch >= '0' && ch <= '9'){
                        state = 1;
                    }else if (ch == 'E' || ch == 'e'){
                        state = 10;
                    }else if (ch == '.'){
                        state = 8;
                    }else if (WordUtil.isVacuumOrBoundaryOrOperator(ch)){
                        state = 15;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 2: {
                    if (ch == 'x' || ch == 'X'){
                        state = 5;
                    }else if (ch >= '0' && ch <= '7') {
                        state = 3;
                    }else if (ch == '.') {
                        state = 8;
                    }else if(WordUtil.isVacuumOrBoundaryOrOperator(ch)) {
                        state = 15;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 3: {
                    if (ch >= '0' && ch <= '7') {
                        state = 3;
                    } else if (WordUtil.isVacuumOrBoundaryOrOperator(ch)) {
                        state = 4;
                    } else {
                        state = 16;
                    }
                    break;
                }
                case 5: {
                    if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
                        state = 6;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 6: {
                    if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
                        state = 6;
                    }else if (WordUtil.isVacuumOrBoundaryOrOperator(ch)) {
                        state = 7;
                    } else {
                        state = 16;
                    }
                    break;
                }
                case 8: {
                    if(ch >= '0' && ch <= '9'){
                        state = 9;
                    }else{
                        state = 16;
                    }
                    break;
                }
                case 9: {
                    if(ch >= '0' && ch <= '9'){
                        state = 9;
                    }else if (ch == 'E' || ch == 'e'){
                        state = 10;
                    }else if (WordUtil.isVacuumOrBoundaryOrOperator(ch)){
                        state = 14;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 10: {
                    if(ch >= '0' && ch <= '9'){
                        state = 12;
                    }else if (ch == '+' || ch == '-'){
                        state = 11;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 11: {
                    if(ch >= '0' && ch <= '9'){
                        state = 12;
                    }else {
                        state = 16;
                    }
                    break;
                }
                case 12: {
                    if(ch >= '0' && ch <= '9'){
                        state = 12;
                    }else if (WordUtil.isVacuumOrBoundaryOrOperator(ch)){
                        state = 13;
                    }else {
                        state = 16;
                    }
                    break;
                }
            }
            i++;
            if(i == txt.length()){
                switch (state){
                    case 1: state = 15; break;
                    case 2: state = 15; break;
                    case 3: state = 4; break;
                    case 5: state = 16; break;
                    case 6: state = 7; break;
                    case 8: state = 16; break;
                    case 9: state = 14; break;
                    case 10: state = 16; break;
                    case 11: state = 16; break;
                    case 12: state = 13; break;
                }
                i++;
                break;
            }
        }
        int code = -1;
        if(state == 16){
            i--;
            error(txt, start);
        }else {
            if (state == 4 || state == 7 || state == 15){
                code = TokenUtil.getTokenCode("整数常量");
            }else if(state == 13 || state == 14){
                code = TokenUtil.getTokenCode("实数常量");
            }
            i--;
            String word = txt.substring(start, i);
            wordList.add(new Word(line, word, code));
        }
    }
    public void error(String txt, int start){
        while (i <= txt.length()){
            if(i == txt.length()){
                String word = txt.substring(start);
                wordList.add(new Word(line, word, -1));
                wrongWordList.add(new Word(line, word, -1));
                break;
            }else{
                if(WordUtil.isVacuumOrBoundaryOrOperator(txt.charAt(i))){
                    String word = txt.substring(start, i);
                    wordList.add(new Word(line, word, -1));
                    wrongWordList.add(new Word(line, word, -1));
                    break;
                }else {
                    i++;
                }
            }
        }
    }
}
